#pragma once

#include "Sock.hpp"
#include "Epoller.hpp"
#include "log.hpp"
#include "util.hpp"
#include "Protocol.hpp"
#include "searcher.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <cerrno>
#include <unordered_map>
#include <functional>
#include <locale>
#include <codecvt>

// 基于Reactor模式，编写一个充分读取和写入的，EPOLL(ET)的Server

class Connection;
class TcpServer;

using func_t = std::function<int(Connection *)>;
using callback_t = std::function<int(Connection *, std::string &)>;

// event
class Connection
{
public:
    int sock_; // 文件描述符
    TcpServer *R_; // 保存自己的this指针
    
    std::string inbuffer_; // 接受缓冲区
    std::string outbuffer_; // 发送缓冲区

    // 回调函数
    func_t recver_; // 处理接收数据
    func_t sender_; // 接收发送数据
    func_t excepter_; // 接收异常数据

public:
    Connection(int sock, TcpServer *r) : sock_(sock), R_(r) {}

    // 设置接收回调函数
    void SetRecver(func_t recver) { recver_ = recver; } 
    // 设置发送回调函数
    void SetSender(func_t sender) { sender_ = sender; }
    // 设置异常回调函数
    void SetExcepter(func_t excepter) { excepter_ = excepter; }

    ~Connection() {}
};

class TcpServer
{
public:
    TcpServer(callback_t cb, int port = 8081) : cb_(cb)
    {
        // 为就绪事件列表分配数组
        revs_ = new struct epoll_event[revs_num];
        // 网络功能
        // 建立监听套接字
        listensock_ = Sock::Socket();
        // 设置为非阻塞
        ns_util::NonBlockUtil::SetNonBlock(listensock_);
        Sock::Bind(listensock_, port);
        Sock::Listen(listensock_);

        // 多路转接, 创建epoll模型
        epfd_ = Epoller::CreateEpoller();

        // 添加listensock匹配的connection, 接收函数绑定了Accepter函数
        // 当接收函数被调用时, 会调用绑定的成员函数Accepter, 第一个参数为默认的this指针, 第二个是占位符(表示调用提供的参数)
        AddConnection(listensock_, EPOLLIN | EPOLLET,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
    }

    // 添加连接
    void AddConnection(int sockfd, uint32_t event, func_t recver, func_t sender, func_t excepter)
    {
        // 设置为边缘触发ET
        // ET模式一定要设置为非阻塞
        if (event & EPOLLET)
            ns_util::NonBlockUtil::SetNonBlock(sockfd);

        // 添加sockfd到epoll中
        Epoller::AddEvent(epfd_, sockfd, event);

        // 将sockfd匹配的Connection添加到unordered_map中
        Connection *conn = new Connection(sockfd, this);
        // 设置对应的回调函数
        conn->SetRecver(recver);
        conn->SetSender(sender);
        conn->SetExcepter(excepter);
        // 添加进去
        connections_.insert(std::make_pair(sockfd, conn));
        LogMessage(DEBUG, "添加新链接到connections成功: %d", sockfd);
    }

    // 创建TcpServer对象后, 调用接收函数会自动先调用该函数
    // 用于接收套接字的连接请求
    int Accepter(Connection *conn)
    {
        while (true)
        {
            std::string clientip;
            uint16_t clientport = 0;
            // 接收连接
            int sockfd = Sock::Accept(conn->sock_, &clientip, &clientport);
            if (sockfd < 0)
            {
                // 错误码为EINTR表示系统调用被信号中断
                if (errno == EINTR)
                    continue;
                // 错误码为EAGAIN或者EWOULDBLOCK表示在非阻塞时, 数据读取完导致的阻塞
                else if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                // 除了上述3种错误码是正常情况, 其它错误码代表确实发生错误
                else
                {
                    LogMessage(WARNING, "accept error");
                    return -1;
                }
            }
            LogMessage(DEBUG, "get a new link: %d", sockfd);
            // 默认只设置让epoll关心读事件，不关心写事件
            // 因为最开始的时候，写空间一定是就绪的, 只是在运行中可以回存在写事件条件(写空间被写满)不足
            // 将对应的函数和其对应的方法绑定起来, 使其调用对应的函数时会去调用对应的方法
            AddConnection(sockfd, EPOLLIN | EPOLLET,
                          std::bind(&TcpServer::TcpRecver, this, std::placeholders::_1),
                          std::bind(&TcpServer::TcpSender, this, std::placeholders::_1),
                          std::bind(&TcpServer::TcpExcepter, this, std::placeholders::_1));
        }
        return 0;
    }

    // 接收请求
    int TcpRecver(Connection *conn)
    {
        // 接收数据
        while (true)
        {
            // 将读取到的数据存储在buffer缓冲区中
            char buffer[1024];
            ssize_t s = recv(conn->sock_, buffer, sizeof(buffer) - 1, 0);
            // 读取成功
            if (s > 0)
            {
                buffer[s] = 0;
                conn->inbuffer_ += buffer;
            }
            // 客户端断开连接
            else if (s == 0)
            {
                LogMessage(DEBUG, "client quit");
                conn->excepter_(conn);
                break;
            }
            // 读取完了或者出错了
            else
            {
                std::cerr << errno << " " << strerror(errno) << std::endl;
                // 原因同上
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                // 出错了
                else
                {
                    LogMessage(DEBUG, "recv error: %d:%s", errno, strerror(errno));
                    // 设置异常
                    conn->excepter_(conn);
                    break;
                }
            }
        }

        std::cout << "读取的消息: " << conn->inbuffer_ << std::endl;
        // 将本轮全部读取完毕
        std::vector<std::string> result;
        // 分割消息放入result数组中
        PackageSplit(conn->inbuffer_, &result);


        // 读取消息
        for (auto &message : result)
        {
            // 调用服务器创建时传递过来的回调函数(服务器要实现的服务)
            cb_(conn, message);
        }
        LogMessage(DEBUG, "接收请求成功");
        return 0;
    }

    // 发送响应
    int TcpSender(Connection *conn)
    {
        while(true)
        {
            // 从发送缓冲区中读取数据并将数据发送到套接字sock中
            ssize_t n = send(conn->sock_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
            // 没有发送完
            if(n > 0)
            {
                // 去除已经成功发送的数据
                conn->outbuffer_.erase(0, n);
            }
            // 发送完了或者出错了
            else
            {
                // 原因同上
                if(errno == EINTR) continue;
                else if(errno == EAGAIN || errno == EWOULDBLOCK) break; //发完了，一定是outbuffer清空了吗？不一定(EPOLLOUT打开)
                // 出错了
                else
                {
                    // 设置异常
                    conn->excepter_(conn);
                    LogMessage(DEBUG, "send error: %d:%s", errno, strerror(errno));
                    break;
                }
            }
        }
        LogMessage(DEBUG, "发送响应成功");
        return 0;
    }

    // 处理异常
    int TcpExcepter(Connection *conn)
    {
        // 首先要检查sock连接是否还在, 不在就没必要处理异常了
        if(!IsExists(conn->sock_)) return -1;
        
        // 所有的服务器异常，都会被归类到这里
        // 一定要先从epoll中移除，然后再关闭fd
        // 1. 先从epoll中删除套接字sock
        Epoller::DelEvent(epfd_, conn->sock_);
        LogMessage(DEBUG, "remove epoll event!");
        // 2. 然后再关闭套接字sock
        close(conn->sock_);
        LogMessage(DEBUG, "close fd: %d", conn->sock_);
        // 3. 结束连接
        delete connections_[conn->sock_];
        LogMessage(DEBUG, "delete connection object done");
        // 4. 删除这个异常连接
        connections_.erase(conn->sock_);
        LogMessage(DEBUG, "erase connection from connections");

        return 0;
    }

    // 判断该sock是否存在
    bool IsExists(int sock)
    {
        // 查找当前sock是否还在(可能会因为一些异常原因断掉)
        auto iter = connections_.find(sock);
        if (iter == connections_.end())
            return false;
        else
            return true;
    }

    // 打开或者关闭读和写(不同事件不同)
    // EnableReadWrite(sock, true, false);
    // EnableReadWrite(sock, true, true);
    void EnableReadWrite(int sock, bool readable, bool writeable)
    {
        uint32_t event = 0;
        event |= (readable ? EPOLLIN : 0);
        event |= (writeable ? EPOLLOUT : 0);
        // 修改事件方式
        Epoller::ModEvent(epfd_, sock, event);
    }

    // 根据就绪事件，将事件进行事件派发
    void Dispatcher()
    {
        // 获取就绪事件列表
        int n = Epoller::LoopOnce(epfd_, revs_, revs_num);
        for (int i = 0; i < n; i++)
        {
            // 从就绪事件列表中获取fd
            int sock = revs_[i].data.fd;
            LogMessage(DEBUG, "获取就绪成功");
            // 获取事件类型
            uint32_t revent = revs_[i].events;
            // 发生了挂起或错误, 尝试读写
            if(revent & EPOLLHUP) revent |= (EPOLLIN|EPOLLOUT);
            if(revent & EPOLLERR) revent |= (EPOLLIN|EPOLLOUT);

            // 读
            if (revent & EPOLLIN)
            {
                // 存在sock和事件recver_, 就调用接回调收函数处理结束数据
                if (IsExists(sock) && connections_[sock]->recver_)
                    connections_[sock]->recver_(connections_[sock]);
                    
            }
            // 写
            if (revent & EPOLLOUT)
            {
                // 存在sock和事件recver_, 就调用发送回调函数处理发送数据
                if (IsExists(sock) && connections_[sock]->sender_)
                    connections_[sock]->sender_(connections_[sock]);
            }
        }
    }

    // 运行服务器
    void Run()
    {
        while (true)
        {
            // 派发任务
            Dispatcher();
        }
    }

    ~TcpServer()
    {
        // 关闭监听套接字和epoll
        if (listensock_ != -1)
            close(listensock_);
        if (epfd_ != -1)
            close(epfd_);
        delete[] revs_;
    }

private:
    static const int revs_num = 64;
    // 1. 网络socket
    int listensock_;
    // 2. epoll
    int epfd_;
    // 3. 将epoll和上层代码进行结合
    std::unordered_map<int, Connection*> connections_;
    // 4. 就绪事件列表
    struct epoll_event *revs_;
    // 5. 设置完整报文的处理方法
    callback_t cb_;
};